#include "curl_http_client.h"

#include "curl_response.h"
#include "string_utils.h"

#include <algorithm>
#include <cstddef>
#include <cstring>
#include <curl/curl.h>
#include <iostream>
#include <memory>
#include <string>
#include <utility>

CurlHttpClient::CurlHttpClient()
{
    curl_global_init(CURL_GLOBAL_ALL);
}

CurlHttpClient::~CurlHttpClient()
{
    curl_global_cleanup();
}

std::shared_ptr<curl_slist> CurlHttpClient::createHeaders(std::vector<std::string> &&property)
{
    return createHeaders(property);
}

std::shared_ptr<curl_slist> CurlHttpClient::createHeaders(const std::vector<std::string> &property)
{
    struct curl_slist *headers = nullptr;
    for (int i = 0; i < property.size(); i++) {
        headers = curl_slist_append(headers, property[i].c_str());
    }
    return std::shared_ptr<curl_slist>(headers, [](curl_slist *p) {
        if (p)
            curl_slist_free_all(p);
    });
}

bool CurlHttpClient::getMethod(CurlResponse &res, const std::string &url, const curl_slist *headers)
{
    // 设置参数
    CURL *curl = curl_easy_init();
    if (!curl) {
        res.errmsg = "curl init failed!";
        return false;
    }

    WriteFuncUserData user_data;
    user_data.curl = curl;
    user_data.resp = &res;

    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
    curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET");
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
    // 连接超时设置，设定时间内接口未调用成功，断开连接
    curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS, 10);
    // 接收数据超时设置，设定时间内数据未接收完，直接退出
    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 60);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&user_data);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, receiveCB);

    CURLcode retCode = curl_easy_perform(curl);
    curl_easy_cleanup(curl);
    if (retCode != CURLE_OK) {
        res.errmsg = curl_easy_strerror(retCode);
        return false;
    }
    return true;
}

bool CurlHttpClient::postMethod(CurlResponse &res, const std::string &url, const curl_slist *headers, const std::string &req)
{
    CURL *curl = curl_easy_init();
    if (!curl) {
        res.errmsg = "curl init failed!";
        return false;
    }

    WriteFuncUserData user_data;
    user_data.curl = curl;
    user_data.resp = &res;

    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
    curl_easy_setopt(curl, CURLOPT_POST, 1L);
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, req.c_str());
    curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, req.length());
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
    // 连接超时设置，设定时间内接口未调用成功，断开连接
    curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS, 10);
    // 接收数据超时设置，设定时间内数据未接收完，直接退出
    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 60);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&user_data);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, receiveCB);

    CURLcode retCode = curl_easy_perform(curl);
    curl_easy_cleanup(curl);
    if (retCode != CURLE_OK) {
        res.errmsg = curl_easy_strerror(retCode);
        return false;
    }
    return true;
}

bool CurlHttpClient::getMethodMultipart(const std::string &url,
                                        const curl_slist  *headers,
                                        DataCallback      *callback,
                                        void              *args,
                                        std::string       &errmsg,
                                        int                curlauth,
                                        std::string        userpwd)
{
    // 设置参数
    CURL *curl = curl_easy_init();
    if (!curl) {
        errmsg = "curl init failed!";
        return false;
    }

    WriteFuncUserData user_data;
    user_data.curl            = curl;
    user_data.resp            = args;
    user_data.callback        = callback;
    user_data.buffer          = new char[1024000];
    user_data.buffer_capacity = 1024000;

    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
    curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
    curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L);
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
    curl_easy_setopt(curl, CURLOPT_HEADERDATA, (void *)&user_data);
    curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, multipartHeaderCB);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&user_data);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, multipartWriteCB);

    if (curlauth == CURLAUTH_DIGEST) {
        curl_easy_setopt(curl, CURLOPT_USERPWD, userpwd.c_str());
        curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
    }

    CURLcode retCode = curl_easy_perform(curl);
    curl_easy_cleanup(curl);
    if (retCode != CURLE_OK) {
        errmsg = curl_easy_strerror(retCode);
        return false;
    }
    return true;
}

int CurlHttpClient::headerProcess(std::unordered_map<std::string, std::string> &headers, const std::string &header_str)
{
    std::vector<std::string> header_list = str_split(header_str, "\r\n");
    for (int i = 0; i < header_list.size(); i++) {
        auto header_pair = str_split(header_list[i], ":");
        if (header_pair.size() != 2) {
            std::cout << "Error header: " << header_list[i] << std::endl;
            continue;
        }

        lower_case(header_pair[0]);
        header_pair[1] = str_trim(header_pair[1], ' ');
        headers.insert(std::make_pair(header_pair[0], header_pair[1]));
    }
    return headers.size();
}

size_t CurlHttpClient::multipartHeaderCB(void *contents, size_t size, size_t nmemb, void *stream)
{
    WriteFuncUserData *user_data  = static_cast<WriteFuncUserData *>(stream);
    char              *data_start = static_cast<char *>(contents);
    if (user_data->boundary.empty()) {
        if (strncmp((char *)contents, "Content-Type:", 13) == 0 || strncmp((char *)contents, "content-type:", 13) == 0) {
            char *boundary_start = strstr((char *)contents, "boundary=");
            if (boundary_start) {
                boundary_start += 9;
                char  *boundary_end = strchr(boundary_start, ';');
                size_t boundary_len = boundary_end ? size_t(boundary_end - boundary_start) : strlen(boundary_start);
                user_data->boundary.resize(boundary_len);
                memcpy((void *)user_data->boundary.data(), boundary_start, boundary_len);

                // 去除末尾的 \r\n
                auto it = std::find_if(user_data->boundary.rbegin(), user_data->boundary.rend(), [](char c) {
                    return c != '\r' && c != '\n';
                });
                user_data->boundary.erase(it.base(), user_data->boundary.end());
            }
        }
    }
    return size * nmemb;
}

size_t CurlHttpClient::multipartWriteCB(void *contents, size_t size, size_t nmemb, void *stream)
{
    WriteFuncUserData *user_data  = static_cast<WriteFuncUserData *>(stream);
    char              *start_data = (char *)contents;
    size_t             real_size  = size * nmemb;
    if (user_data->buffer_size > 0) {
        memcpy(user_data->buffer + user_data->buffer_size, start_data, real_size);
        start_data = user_data->buffer;
        user_data->buffer_size += real_size;
        real_size = user_data->buffer_size;
    }

    char *current = start_data;
    while (real_size > 0) {
        size_t      processed = 0;
        std::string boundary  = split_of_end_string(current, user_data->boundary.c_str(), true);
        if (boundary.empty())
            break;

        processed = boundary.length();
        for (; processed < real_size; ++processed) {
            if (current[processed] != '\r' && current[processed] != '\n')
                break;
        }

        // 获取HTTP头
        std::string header = split_of_end_string(current + processed, "\r\n\r\n");
        if (header.empty()) {
            header = split_of_end_string(current + processed, "\n\n");
        }
        if (header.empty())
            break;
        processed += header.length();

        for (; processed < real_size; ++processed) {
            if (current[processed] != '\r' && current[processed] != '\n')
                break;
        }

        std::string                                  message;
        std::unordered_map<std::string, std::string> headerMap;
        headerProcess(headerMap, header);
        if (headerMap.find("content-length") != headerMap.end()) {
            int length = std::stoi(headerMap["content-length"]);
            message.resize(length);

            memcpy((char *)message.data(), current + processed, length);
            processed += length;
        } else {
            // 获取数据
            message = split_of_end_string(current + processed, "\r\n\r\n", true);
            if (message.empty()) {
                message = split_of_end_string(current + processed, "\n\n", true);
            }
            if (message.empty())
                break;
            processed += message.length();
        }

        // 回调进行数据处理
        if (user_data->callback)
            user_data->callback(message.c_str(), message.length(), user_data->resp);
        current = current + processed;
        real_size -= processed;
    }

    // 剩余数据拷贝到 user_data->buffer
    user_data->buffer_size = real_size;
    if (real_size > 0) {
        memcpy(user_data->buffer, current, real_size);
    }
    return size * nmemb;
}

size_t CurlHttpClient::receiveCB(void *contents, size_t size, size_t nmemb, void *stream)
{
    WriteFuncUserData *user_data = static_cast<WriteFuncUserData *>(stream);
    CurlResponse      *curl_res  = static_cast<CurlResponse *>(user_data->resp);
    curl_res->response           = std::string((char *)contents);
    return size * nmemb;
}